package com.leyou.order.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.auth.pojo.UserInfo;
import com.leyou.common.auth.utils.UserHolder;
import com.leyou.common.constant.LyConstants;
import com.leyou.common.exception.LyException;
import com.leyou.common.exception.enums.ExceptionEnum;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.item.client.ItemClient;
import com.leyou.item.entity.Sku;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.order.vo.OrderDetailVO;
import com.leyou.order.vo.OrderLogisticsVO;
import com.leyou.order.vo.OrderVO;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
// import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author LT
 * @version 1.0.0
 * @ClassName OrderService.java
 * @Description TODO 订单服务类
 * @createTime 2020年05月21日 10:43:00
 */

@Slf4j
@Service
@Transactional
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper detailMapper;

    @Autowired
    private OrderLogisticsMapper logisticsMapper;

    @Autowired
    private IdWorker idworker;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private PayHelper payHelper;

    /**
     * 创建订单接口
     *
     * @param orderDTO 接收请求的参数
     * @return 返回订单ID
     */
    // @GlobalTransactional // 开启Seata分布式事务
    @Transactional  // 本地事务注解
    public Long createOrder(OrderDTO orderDTO) {
        // 0.生成订单的id(雪花id)
        long orderId = idworker.nextId();
        Long userId = UserHolder.getUser().getId();  // 当前登录用户的id

        // 1.保存订单
        Order order = new Order();
        // 1.1 设置固定值的
        order.setOrderId(orderId);
        order.setPostFee(0L);                             // 乐优大卖场全场包邮
        order.setPaymentType(orderDTO.getPaymentType());  // 支付类型
        order.setUserId(userId);                          // 当前用户
        order.setStatus(OrderStatusEnum.INIT.value());    // 订单状态
        order.setInvoiceType(0);                          // 无发票
        order.setSourceType(2);                           // 来自pc端

        // ==========================================================
        // 1.2 设置经过计算得出的值 : 得出每个sku的价格
        // 1.2.1 获取购物车数据
        List<CartDTO> carts = orderDTO.getCarts();
        // 为了方便我们后面通过skuid获取购买的商品的数量，我们把list集合转成map
        Map<Long, Integer> cartMap = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
        // 1.2.2 获取所有的sku的id
        List<Long> skuIdList = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
        // 1.2.3 批量查询sku
        List<Sku> skuList = itemClient.findSkusByIds(skuIdList);
        if (CollectionUtils.isEmpty(skuList)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        // 1.2.4 计算总金额
        long total = skuList.stream().mapToLong(sku -> sku.getPrice() * cartMap.get(sku.getId())).sum(); // stream流
        /*
           Long total = 0;
           for (Sku sku : skuList){
            total += sku.getPrice()* cartMap.get(sku.getId());
            // total = total+ sku.getPrice()* cartMap.get(sku.getId());
           }
         */
        order.setTotalFee(total); // 总金额计算得出
        // order.setTotalFee(null);
        order.setActualFee(1L);   // TODO： 实付金额 = 总金额 - 活动优惠的金额 ，现在我们写死一分钱
        // 1.3 保存
        int count = orderMapper.insertSelective(order);
        if (count != 1) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        // 2.保存订单的条目：批量插入
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (Sku sku : skuList) {
            OrderDetail orderDetail = new OrderDetail();
            // 2.1 设置固定值的
            orderDetail.setId(idworker.nextId());         // 雪花id
            orderDetail.setOrderId(orderId);              // 订单id
            orderDetail.setSkuId(sku.getId());            // skuid
            orderDetail.setNum(cartMap.get(sku.getId())); // 购买量
            orderDetail.setTitle(sku.getTitle());
            orderDetail.setPrice(sku.getPrice());
            orderDetail.setOwnSpec(sku.getOwnSpec());
            orderDetail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            orderDetail.setCreateTime(new Date());  // time
            orderDetail.setUpdateTime(new Date());
            // 添加orderDetail
            orderDetailList.add(orderDetail);
        }
        count = detailMapper.insertList(orderDetailList);
        if (count != orderDetailList.size()) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        // 3.保存订单的物流表信息
        // 3.1 查询地址
        AddressDTO addressDTO = userClient.queryAddressById(userId, orderDTO.getAddressId());
        // 3.2 创建物流对象
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);
        orderLogistics.setOrderId(orderId);
        // 3.3 保存物流信息
        count = logisticsMapper.insertSelective(orderLogistics);
        if (count != 1) {
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
        // 4.减去库存
        itemClient.minusStock(cartMap);

        // int i = 1 / 0; // 模拟异常(测试减去库存)

        // 5.返回订单的id(雪花id)
        // 遗留功能：清空购物车。什么时候清空？下单【对卖家不友好】，支付【对买家不友好】。
        return orderId;
    }

    /**
     * 根据订单编号查询订单
     *
     * @param id 订单编号
     * @return OrderVO对象
     */
    public OrderVO findOrderById(Long id) {
        // 1.查询订单
        Order order = orderMapper.selectByPrimaryKey(id);
        // 1.1 判空
        if (order == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 1.2 判断订单是否是当前用户的
        Long userId = UserHolder.getUser().getId();
        if (!StringUtils.equals(userId.toString(), order.getUserId().toString())) {
            throw new LyException(ExceptionEnum.ORDER_ERROR);
        }
        // 1.3 把Order转成Vo
        OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);
        // 2. 查询物流表
        OrderLogistics orderLogistics = logisticsMapper.selectByPrimaryKey(id);
        if (orderLogistics == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 3.查询订单条目
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderId(id);
        List<OrderDetail> orderDetailList = detailMapper.select(orderDetail);
        if (CollectionUtils.isEmpty(orderDetailList)) {
            throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);
        }
        // 4.封装到vo中并返回
        orderVO.setLogistics(BeanHelper.copyProperties(orderLogistics, OrderLogisticsVO.class));
        orderVO.setDetailList(BeanHelper.copyWithCollection(orderDetailList, OrderDetailVO.class));
        return orderVO;
    }

    /**
     * 获取微信支付的链接
     *
     * @param id
     * @return
     */
    public String getPayUrl(Long id) {
        // 0.先去redis中查询，没有这个url，再去微信获取 TODO (优化1，存放到redis)
        String key = LyConstants.PAY_URL_PRE + id;
        try {
            Boolean bool = redisTemplate.hasKey(key);
            if (bool != null && bool) {
                return redisTemplate.opsForValue().get(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【订单微服务，redis挂了，赶紧去维护】");
        }

        // 1.查询订单信息
        Order order = orderMapper.selectByPrimaryKey(id);
        // 1.1 判空
        if (id == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 1.2 判断订单的状态 TODO(优化2，就返回一个状态)
        if (!(OrderStatusEnum.INIT.value() == order.getStatus())) {
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }
        // 2.查询订单详情
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderId(id);
        List<OrderDetail> orderDetails = detailMapper.select(orderDetail);
        // 2.1 判空
        if (CollectionUtils.isEmpty(orderDetails)) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 2.2 拼接描述信息
        String desc = orderDetails.stream().map(OrderDetail::getTitle).collect(Collectors.joining());
        if (StringUtils.isNotBlank(desc)) {
            if (desc.length() > 50) {
                desc = StringUtils.substring(desc, 0, 50);
            }
        }
        // 3.支付的url获取
        String payUrl = payHelper.getPayUrl(id, order.getActualFee(), desc);
        // 4、获取到url之后，存入redis中
        try {
            redisTemplate.opsForValue().set(key, payUrl, 2, TimeUnit.HOURS);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【订单微服务，redis挂了，赶紧去维护】");
        }
        return payUrl;
    }

    /**
     * 微信通知支付结果
     *
     * @param msg
     */
    public void payNotify(Map<String, String> msg) {
        // 1、校验是否是成功
        payHelper.checkResultCode(msg);
        // 2、校验签名
        payHelper.checkSignature(msg);
        // 3、获取微信返回的乐优的订单号和订单金额
        Long orderId = Long.valueOf(msg.get("out_trade_no"));
        Long total_fee = Long.valueOf(msg.get("total_fee"));
        // 4、获取订单
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 5、接口的幂等性校验： N请求过来，结果一样
        if (order.getStatus() != OrderStatusEnum.INIT.value()) {
            throw new LyException(ExceptionEnum.INVALID_ORDER_STATUS);
        }

        // 6、判断订单的金额是否一致
        if (!order.getActualFee().equals(total_fee)) {
            throw new LyException(400, "订单金额不一致");
        }
        // 7、完成状态的更改
        order.setStatus(OrderStatusEnum.PAY_UP.value());
        int count = orderMapper.updateByPrimaryKeySelective(order);
        if (count != 1) {
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
        log.info("【微信支付成功】订单号：{} , 总金额:{}", orderId, total_fee);
    }

    /**
     * 查询订单的状态
     *
     * @param id 订单编号
     * @return 返回订单的状态
     */
    public Integer findOrderState(Long id) {
        Order order = orderMapper.selectByPrimaryKey(id);
        if (order == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }

    /**
     * --------------------------------------------订单优化---------------------------------------------------------
     * 根据ThreadLocal记录的用户查询其所有订单
     *
     * @return
     */
    public PageResult<OrderVO> findOrdersByPage() {
        UserInfo user = UserHolder.getUser();
        Long id = user.getId();
        Order record = new Order();
        record.setUserId(id);
        List<Order> select = orderMapper.select(record);
        PageResult<OrderVO> orderVOPageResult = getOrderVOPageResult(select);

        return orderVOPageResult;
    }

    /**
     * 根据传入的status分页查询用户相应的订单(1234枚举)
     *
     * @param status
     * @return
     */
    public PageResult<OrderVO> findOrdersByPageByStatus(Integer status) {
        UserInfo userInfo = UserHolder.getUser();
        Order order = new Order();
        order.setUserId(userInfo.getId());
        order.setStatus(status);
        List<Order> orderList = orderMapper.select(order);
        PageResult<OrderVO> orderVOPageResult = getOrderVOPageResult(orderList);
        return orderVOPageResult;
    }

    /**
     * 把查询并封装list<order>的代码抽取出来
     *
     * @param select
     * @return
     */
    private PageResult<OrderVO> getOrderVOPageResult(List<Order> select) {

        //====================
        List<OrderVO> orderVOS = new ArrayList();
        ;
//        遍历
        for (Order order : select) {
            //查询订单地址
            OrderLogistics orderLogistics = logisticsMapper.selectByPrimaryKey(order.getOrderId());
            if (orderLogistics == null) {
                throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
            }
            // 3、查询订单条目
            OrderDetail tempRecord = new OrderDetail();
            tempRecord.setOrderId(order.getOrderId());
            List<OrderDetail> orderDetails = detailMapper.select(tempRecord);
            if (CollectionUtils.isEmpty(orderDetails)) {
                throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);
            }
            //BeanHelper 爆改对象并注入VO对象属性值
            OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);
            orderVO.setLogistics(BeanHelper.copyProperties(orderLogistics, OrderLogisticsVO.class));
            orderVO.setDetailList(BeanHelper.copyWithCollection(orderDetails, OrderDetailVO.class));
            orderVOS.add(orderVO);
        }
//        ===========封装成PageResult=============
//        分页
        PageHelper.startPage(1, 3);
//       获取分页数据
        PageInfo<OrderVO> pageInfo = new PageInfo<>(orderVOS);
//        总页数
        int pages = pageInfo.getPages();
//        总记录数
        long total = pageInfo.getTotal();
//        当前页数据
        List<OrderVO> list = pageInfo.getList();
        PageResult<OrderVO> orderVOPageResult = new PageResult<>(total, pages, list);
//        =========return=====
        System.out.println(orderVOPageResult.toString());
        return orderVOPageResult;
    }

    /**
     * 通过关键字搜索查找订单
     *
     * @param keyWord
     * @return
     */
    public PageResult<OrderVO> findOrdersByKeyWordSearch(String keyWord) {
        List<Order> orderList = orderMapper.findOrdersByKeyWord("%" + keyWord + "%");
        PageResult<OrderVO> orderVOPageResult = getOrderVOPageResult(orderList);
        System.out.println("orderByKeyWord = ");
        return orderVOPageResult;
    }
}
